package com.checkCode.store;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 内存持久化存储验证码key和value
 *
 * @author 大忽悠
 * @create 2023/2/9 21:21
 */
@Component
public class MemoryCodeStore implements CodeStore {
    /**
     * 验证码key,验证码value
     */
    private Map<String, String> codeCache = new ConcurrentHashMap<>();
    /**
     * 验证码key,验证码过期时间戳
     */
    private Map<String, Long> codeExpireTimeCache = new ConcurrentHashMap<>();

    /**
     * @param key    验证码key
     * @param code   验证码
     * @param expire 过期秒数
     */
    @Override
    public void store(String key, String code, Long expire) {
        codeCache.put(key, code);
        codeExpireTimeCache.put(key, LocalDateTime.now().plusSeconds(expire).toEpochSecond(ZoneOffset.of("+8")));
    }

    /**
     * @param key 验证码key
     * @return code or null ,验证码失效或者不存在,则返回null
     */
    @Override
    public String load(String key) {
        Long expireTimestamp = codeExpireTimeCache.get(key);
        //验证码key不存在,或者已经失效了
        if (expireTimestamp == null || LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) > expireTimestamp) {
            codeCache.remove(key);
            codeExpireTimeCache.remove(key);
            return null;
        }
        return codeCache.get(key);
    }

    @Override
    public void remove(String key) {
        codeExpireTimeCache.remove(key);
        codeCache.remove(key);
    }

    /**
     * 每隔30分钟清理一次
     */
    @Scheduled(fixedDelay = 30 * 60 * 1000)
    public void clearCache() {
        List<String> expireKeys = new ArrayList<>();
        codeExpireTimeCache.forEach((key, expireTimeStamp) -> {
            if (LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) > expireTimeStamp) {
                expireKeys.add(key);
            }
        });
        expireKeys.forEach((key) -> {
            codeExpireTimeCache.remove(key);
            codeCache.remove(key);
        });
    }
}
